package beerAgent;

import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioInputStream;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;

import neuralNetwork.CTRNN;
import beerAgent.BeerModel.Scenario;
import evolutionaryProblems.RunPhenotype;
import evolutionaryProblems.RunningStatus;
import genotype.BeerGenotype;

public class RunBeerPhenotype extends RunPhenotype {

	private BeerPhenotype phenotype; 
	private BeerGameModel beerGameModel; 
	private int gameNr = 0;
	private int iterations = -1;
	private int iteration; 
	private BeerGame game; 
	
	private BeerParameters parameters;
	private BeerGame[] games;
	private CTRNN network;
	private BeerFitnessFunction fitnessFunction;
	
	private AudioFormat audioFormat;
	private AudioInputStream audioInputStream;
	private SourceDataLine sourceDataLine;
	private boolean stopPlayback = false;
	private int nofCatchLarge;
	private int nofCatchSmall;
	private int nofMissLarge;
	private int nofMissSmall;
	private int nofHitsByLarge;

	
	public RunBeerPhenotype(BeerGameModel beerGameModel, BeerPhenotype phenotype) {
		status = RunningStatus.PAUSED; 
		this.beerGameModel = beerGameModel; 
		this.phenotype = phenotype; 
		this.parameters = (BeerParameters) phenotype.getGenotype().getParameters();
		setup();
	}

	private void setup(){
//		FlatlandPhenotype fPhenotype = (FlatlandPhenotype) phenotype;
		fitnessFunction = ((BeerFitnessFunction) phenotype.fitnessFunction); 
		games = fitnessFunction.getMaps(phenotype.getCurrentFitnessFunction());
		gameNr = 0; 
	}

	@Override
	protected void doTimeStep() {
		checkSetup(); 
		if (status == RunningStatus.TERMINATING) {
			return; 
		}
		
		boolean[] sensors = game.getSensors();
		double[] outputs = network.update(sensors); 
		int steps = fitnessFunction.getSteps(outputs);
		BeerModel.Action action = fitnessFunction.selectAction(outputs, steps);
		try {
			synchronized (this) {
				wait(beerGameModel.getTimePerFrame());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
			status = RunningStatus.FINISHED; 
		}
				
		String path = new File("").getAbsolutePath();
		FileSystem fileSys = FileSystems.getDefault();
		String sep = fileSys.getSeparator();
		path += (sep + "src" + sep + "audio" + sep);
		Scenario moveResult = game.move(action, steps);

		switch (moveResult) {
		case CATCH_LARGE:
			nofCatchLarge++;
			playAudio(path + "no.wav");
			print(nofCatchSmall, nofCatchLarge, nofMissLarge, nofMissSmall, nofHitsByLarge);
			break;
		case CATCH_SMALL:
			nofCatchSmall++;
			playAudio(path + "yes.wav");
			print(nofCatchSmall, nofCatchLarge, nofMissLarge, nofMissSmall, nofHitsByLarge);
			break;
		case MISS_LARGE:
			nofMissLarge++;
			print(nofCatchSmall, nofCatchLarge, nofMissLarge, nofMissSmall, nofHitsByLarge);
			playAudio(path + "yes.wav");
			break;
		case MISS_SMALL:
			nofMissSmall++;
			print(nofCatchSmall, nofCatchLarge, nofMissLarge, nofMissSmall, nofHitsByLarge);
			playAudio(path + "no.wav");
			break;
		case HIT_BY_LARGE:
			nofHitsByLarge++;
			print(nofCatchSmall, nofCatchLarge, nofMissLarge, nofMissSmall, nofHitsByLarge);
			playAudio(path + "no.wav");
		default:
			break;
		}
		
	}
	
	
	private void print(int nofCatchSmall, int nofCatchLarge, int nofMissLarge, int nofMissSmall, int nofHitsByLarge) {
		System.out.println("nofCatchSmall: " + nofCatchSmall
				+ " nofCatchLarge: " + nofCatchLarge + " nofMissSmall: "
				+ nofMissSmall + " nofMissLarge: " + nofMissLarge
				+ " nofHitsByLarge: " + nofHitsByLarge);
	}

	private void checkSetup() {
		if (++iteration > iterations) {
			iteration = 0;
			if (games.length == gameNr) {
				status = RunningStatus.TERMINATING;
				return;
			}
			game = games[gameNr++].copy();
			game.setDraw(true);
			beerGameModel.setMap(game.getGamePanel());
			beerGameModel.pack();

			network = new CTRNN(parameters.getNetworkLayout());
			Double[] weights = ((BeerGenotype) phenotype.getGenotype())
					.getArrayGenotype();
			network.setWeights(convertDtod(weights));

			iterations = beerGameModel.getIterationsPerMap();
			nofCatchLarge = 0;
			nofCatchSmall = 0;
			nofMissLarge = 0;
			nofMissSmall = 0;
			nofHitsByLarge = 0;
		}
	}

	private void playAudio(String filename) {
		try {
			File soundFile = new File(filename);
			audioInputStream = AudioSystem.getAudioInputStream(soundFile);
			audioFormat = audioInputStream.getFormat();

			DataLine.Info dataLineInfo = new DataLine.Info(
					SourceDataLine.class, audioFormat);

			sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo);

			new PlayThread().start();
		} catch (Exception e) {
			e.printStackTrace();
			System.exit(0);
		}
	}

	class PlayThread extends Thread {
		byte tempBuffer[] = new byte[10000];

		public void run() {
			try {
				sourceDataLine.open(audioFormat);
				sourceDataLine.start();

				int cnt;

				while ((cnt = audioInputStream.read(tempBuffer, 0,
						tempBuffer.length)) != -1 && stopPlayback == false) {
					if (cnt > 0) {

						sourceDataLine.write(tempBuffer, 0, cnt);
					}
				}
				sourceDataLine.drain();
				sourceDataLine.close();

				stopPlayback = false;
			} catch (Exception e) {
				e.printStackTrace();
				System.exit(0);
			}
		}
	}
}
